OpenGL ES入门(二) ——画个三角形

使用OpenGL ES在iOS平台上画三角形

演示在iOS平台上,使用OpenGL ES 3.0绘制一个三角形

流程

  1. 准备渲染环境:

  2. 编写Shader

  3. 准备数据:

  4. 开始绘制

  5. 释放内存

一、准备渲染环境

1.引入OpenGL ES框架

#import <OpenGLES/ES3/gl.h>

2.准备绘图的Layer

新建一个视图类所有的绘制都在内部完成,指定它的Layer为CAEAGLLayer,只有此类型的Layer才支持OpenGL ES的绘制

1
2
3
+ (Class)layerClass {
return [CAEAGLLayer class];
}

配置Layer属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
CAEAGLLayer *glLayer = (CAEAGLLayer *)self.layer;
NSDictionary *drawableProperties = @{
kEAGLDrawablePropertyRetainedBacking: @(NO),
kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGBA8
};
/**
* kEAGLDrawablePropertyRetainedBacking:不维持渲染内容
* kEAGLDrawablePropertyColorFormat:颜色缓冲格式 32-bit RGBA格式
*/
glLayer.drawableProperties = drawableProperties;

glLayer.contentsScale = [UIScreen mainScreen].scale;
//Layer默认是透明的 设置为不透明内容才可见
glLayer.opaque = YES;

3.准备绘制上下文环境

1
2
3
4
5
6
7
8
- (void)setUpContext {

// 指定 OpenGL 渲染 API 的版本,在这里我们使用 OpenGL ES 3.0
_glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];

// 设置为当前上下文
[EAGLContext setCurrentContext:_glContext];
}

4.准备渲染缓存空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//申请颜色渲染缓存空间
GLuint colorRenderBuffer;
glGenBuffers(1, &colorRenderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, colorRenderBuffer);
self.colorRenderBuffer = colorRenderBuffer;
// 为 color renderbuffer 分配存储空间
[self.glContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];

//申请帧渲染缓存空间
GLuint frameBuffer;
glGenFramebuffers(1, &frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
self.colorFrameBuffer = frameBuffer;
// 将 _colorRenderBuffer 装配到 GL_COLOR_ATTACHMENT0 这个装配点上
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderBuffer);

二、编写Shader

1.编写顶点着色器

1
2
3
4
5
6
7
8
9
attribute vec3 a_Position; //顶点坐标属性
attribute vec4 a_Color; //顶点颜色属性

varying highp vec4 v_Color; //传递颜色值到片元着色器接口

void main(void) {
v_Color = a_Color;
gl_Position = vec4(a_Position.x, a_Position.y, a_Position.z, 1.0);
}

2.编写片元着色器

1
2
3
4
5
varying highp vec4 v_Color; //接收传递过来的颜色值

void main(void) {
gl_FragColor = v_Color; //传递给输出变量
}

3.加载着色器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- (GLuint)loadShaderCode:(NSString *)fileName shaderType:(GLenum)type {
NSString *filePath = [[NSBundle mainBundle]pathForResource:fileName ofType:nil];
NSError *error;
NSString *content = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error];
if (error) {
NSLog(@"Load Shader Error:%@",error.localizedDescription);
return 0;
}

const GLchar *stringDatas = [content UTF8String];
GLint stringLength = (GLint)content.length;

//创建ShaderId
GLuint shaderID = glCreateShader(type);

//加载Shader 数据
glShaderSource(shaderID, 1, &stringDatas, &stringLength);

return shaderID;
}

4.编译着色器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- (BOOL)compilingShaderWithShaderIdentifier:(GLuint)shaderId {
//编译 Shader
glCompileShader(shaderId);

//获取编译信息
GLint compileSuccess;
glGetShaderiv(shaderId, GL_COMPILE_STATUS, &compileSuccess);
if (compileSuccess == GL_FALSE) {
GLint infoLength;
glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &infoLength);
GLchar *messages = malloc(sizeof(GLchar *) * infoLength);
glGetShaderInfoLog(shaderId, infoLength, NULL, messages);
NSString *messageString = [NSString stringWithUTF8String:messages];
NSLog(@"Error: Copiled Fail:%@",messageString);
free(messages);
return NO;
}

return YES;

}

5.链接着色器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
- (void)createProgram:(GLuint)vertexShader fragmentShader:(GLuint)fragmentShader {

//创建程序
GLuint program = glCreateProgram();

//附加着色器
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);

//绑定属性所在的位置
const GLuint positionAttributeIndex = 0;
const GLuint colorAttributeIndex = 1;

const GLchar *positionAttributeName = "a_Position";
const GLchar *colorAttributeName = "a_Color";

glBindAttribLocation(program, positionAttributeIndex, positionAttributeName);
glBindAttribLocation(program, colorAttributeIndex, colorAttributeName);

//链接程序
glLinkProgram(program);

GLint linkSuccess;
glGetProgramiv(program, GL_LINK_STATUS, &linkSuccess);
if (linkSuccess == GL_FALSE) { //链接失败
GLint infoLength;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLength);
GLchar *messages = malloc(sizeof(GLchar *) * infoLength);
glGetProgramInfoLog(program, infoLength, NULL, messages);
NSString *messageString = [NSString stringWithUTF8String:messages];
NSLog(@"Error: Program Link Fail:%@",messageString);
free(messages);
return ;
}

//使用当前程序
glUseProgram(program);

//释放Shader 内存
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);

self.program = program;
}

三、准备数据

1.坐标

OpenGL ES坐标系:

OpenGL ES坐标系是一个三维坐标系{x,y,z},三维坐标中心在正方体的几何中心{0,0,0}

整个坐标系是[0,1]的点,也就是说OpenGL中只支持0~1的点

2.生成顶点数据

1
2
3
4
5
6
GLfloat vertexs[] = {
//顶点坐标 //顶点颜色
-0.5, -0.5, 0.0, 1.0,0.0,0.0,1.0,
0.0, 0.5, 0.0, 1.0,0.0,0.0,1.0,
0.5, -0.5, 0.0, 1.0,0.0,0.0,1.0,
};

上面数据定义了三个顶点数据,每个顶点数据包含坐标和顶点颜色.

3.生成顶点缓存对象

1
2
3
4
5
6
7
//生成一个顶点缓存对象
GLuint VBO;
glGenBuffers(1, &VBO);
//绑定到GL_ARRAY_BUFFER缓冲类型上
glBindBuffer(GL_ARRAY_BUFFER, VBO);
//将顶点数据复制到缓存内存中
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexs), vertexs, GL_STATIC_DRAW);

4.定义顶点解析配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const GLuint positionAttributeIndex = 0;
const GLuint colorAttributeIndex = 1;

const GLint positionCount = 3;
const GLint colorCount = 4;

//定义顶点的解析配置
/**
positionAttributeIndex:属性开始位置
positionCount:属性数据长度
GL_FLOAT : 属性值的类型
GL_FALSE : 数据不被标准化 //标准化:将所有数据映射到0-1之间
sizeof(GLfloat) * 7: 步长,每个顶点属性组的长度
(void *)0 : 偏移量 每个顶点从属性组第几个位置取值
*/
//配置顶点位置属性
glVertexAttribPointer(positionAttributeIndex, positionCount, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 7, (void *)0);
//使属性配置生效
glEnableVertexAttribArray(positionAttributeIndex);

//配置顶点颜色属性
glVertexAttribPointer(colorAttributeIndex, colorCount, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 7, (void *)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(colorAttributeIndex);

四、开始绘制

1.开始绘制

1
2
3
4
5
6
7
//开始绘制
/**
GL_TRIANGLES:绘制三角形
0 :起点从第0个位置开始
3 :绘制3个顶点
*/
glDrawArrays(GL_LINES, 0, 3);

2.将绘制内容展现到屏幕上

1
2
//将渲染缓存区的内容显示到屏幕上
[self.glContext presentRenderbuffer:GL_RENDERBUFFER];

五、释放内存

1
2
3
4
//渲染完成释放内存
glDisableVertexAttribArray(positionAttributeIndex);
glDisableVertexAttribArray(colorAttributeIndex);
glDeleteBuffers(1, &VBO);